#include "Logger.h"

#include <iostream>  // For std::cerr.

namespace SE
{
	Logger::~Logger()
	{
		spdlog::drop_all();
		spdlog::shutdown();
	}

	Ref<Logger> Logger::Create(
		const String&		fileName,
		const U32			maxFileSize,
		const U32			maxFile,
		const OutputMode	outputMode,
		const OutputTarget	outputTarget,
		const OutputLevel	outputLevel)
	{
		auto newLogger = CreateRef<Logger>();
		newLogger->mNativeLogger = nullptr;
		try
		{
			Vector<spdlog::sink_ptr> sinks;
			auto outputFormat = "[%Y-%m-%d %H:%M:%S.%e] <thread %t> [%^%l%$]\n[%@,%!]\n%v\n";

			// Set output target.
			if (OutputTarget::Console & outputTarget)
			{
				auto console_sink = CreateRef<spdlog::sinks::stdout_color_sink_mt>();
				console_sink->set_level(spdlog::level::trace);
				console_sink->set_pattern(outputFormat);
				sinks.push_back(console_sink);
			}
			if (OutputTarget::File & outputTarget)
			{
				auto file_sink = CreateRef<spdlog::sinks::rotating_file_sink_mt>(fileName, maxFileSize, maxFile);
				file_sink->set_level(spdlog::level::trace);
				file_sink->set_pattern(outputFormat);
				sinks.push_back(file_sink);
			}

			// Set logger sinks.
			if (OutputMode::ASync == outputMode)
			{
				spdlog::init_thread_pool(102400, 1);
				newLogger->mNativeLogger = CreateRef<spdlog::async_logger>(
					LOG_NAME,
					begin(sinks),
					end(sinks),
					spdlog::thread_pool(),
					spdlog::async_overflow_policy::block);
			}
			else
			{
				newLogger->mNativeLogger = CreateRef<spdlog::logger>(
					LOG_NAME,
					begin(sinks),
					end(sinks));
			}
			newLogger->mNativeLogger->set_level((spdlog::level::level_enum)outputLevel);

			newLogger->mNativeLogger->flush_on(spdlog::level::warn);
			spdlog::flush_every(std::chrono::seconds(3));
			spdlog::register_logger(newLogger->mNativeLogger);
		}
		catch (const spdlog::spdlog_ex& ex)
		{
			std::cerr << "Log initialization failed: " << ex.what() << std::endl;
			return nullptr;
		}
		return newLogger;
	}
} // !namespace SE